package com.simhash;

import java.math.BigInteger;
import java.security.MessageDigest;

public class SimHash128Web {
	/* @函数：String MD5_Hash(String ciyu)
	 * @作用：输入字符串ciyu，计算其128位hash值，并将其以String型返回
	 * */
	public static String MD5_Hash(String ciyu) {
		try{
			// 生成一个MD5加密计算摘要
			MessageDigest md = MessageDigest.getInstance("MD5");
			// 计算md5函数
			//System.out.println("字符串:"+ciyu);
			//System.out.println("字符串的MD5_Hash:"+md.digest(str.getBytes()));
			// digest()最后确定返回md5 hash值，返回值为8为字符串。因为md5 hash值是16位的hex值，实际上就是8位的字符
			// BigInteger函数则将8位的字符串转换成16位hex值，用字符串来表示；得到字符串形式的hash值
			return new BigInteger(1,md.digest(ciyu.getBytes("UTF-8"))).toString(2);
		}catch(Exception e){
			e.printStackTrace();
			return ciyu;
		}
	}
	/* @函数：String Set(String str_zh)
	 * @功能：1.先创建一个存储SimHash值的128数组，并初始化为0
	 * @功能：2.将str字符串分词，并存入临时数组
	 * @功能：3.计算此字符串的SimHash值（加权、但没有降维），存储在数组中
	 * @功能：4.将数组中的SimHash值降维，并以字符串形式返回
	 * */
	public static String Set(String str_zh) {
		//1.先创建一个存储SimHash值的128数组，并初始化为0
		float Hash_SZ[] = new float[128];
		for(int i=0;i<Hash_SZ.length;i++)
			Hash_SZ[i]=0;
		//2.将str字符串分词，并将词和权重存入临时数组
		String[] word_and_tfidf = str_zh.split("/");//先分割
		String[] word = new String[word_and_tfidf.length];//创建一个词数组，大小为词个数
		String[] tfidf = new String[word_and_tfidf.length];//创建一个权重数组，大小为词个数
		//分割词，存入数组
		for(int i=0; i<word_and_tfidf.length; i++){
			String[] linshi = word_and_tfidf[i].split("##");
			word[i] = linshi[0];
			tfidf[i] = linshi[1];
			//System.out.println(word[i]+" "+tfidf[i]);
		}
		//3.计算此字符串的SimHash值（加权、但没有降维），存储在数组中
		for(int i=0;i<word.length;i++){//循环传入字符串的每个词
			String str_hash = SimHash128Web.MD5_Hash(word[i]);//先计算每一个词的Hash值（128位）
			//MD5哈希计算时，二进制转换若最高位为7以下，也就是转换成二进制最高位为0，会不存储该0，导致后面程序出错
			//这里主要是为了保证它是128位的二进制
			if(str_hash.length() < 128){
				int que = 128 - str_hash.length();
				for(int j=0;j<que;j++){
					str_hash = "0" + str_hash;
				}
			}
			//System.out.println(str_hash);//输出该词的128位MD5哈希值
			char str_hash_fb[]=str_hash.toCharArray();//将该词的哈希值转为数组，方便检查
			//对每个词的Hash值（128位）求j位是1还是0,1的话加上该词的权重，0的话减去该词的权重
			for(int j=0;j<Hash_SZ.length;j++){
				if(str_hash_fb[j] == '1'){
					Hash_SZ[j] += Float.parseFloat(tfidf[i]);//Hash_SZ中，0是最高位，依次排低
				}else{
					Hash_SZ[j] -= Float.parseFloat(tfidf[i]);
				}
				//System.out.print(Hash_SZ[j]);
			}
			//System.out.println();
		}
		//4.将数组中的SimHash值降维，并以字符串形式返回
		String SimHash_number="";//存储SimHash值
		for(int i=0;i<Hash_SZ.length;i++){//从高位到低位
			//System.out.print(Hash_SZ[i]+" ");//输出未降维的串
			if(Hash_SZ[i]<=0)//小于等于0，就取0
				SimHash_number += "0";
			else//大于0，就取1
				SimHash_number += "1";
		}
		//System.out.println("");//换行
		System.out.println("字符串处理完毕，已生成该字符串的SimHash值！");
		System.out.println(SimHash_number);
		System.out.println("");
		return SimHash_number;
	}
	/* @函数：int HMJL(String a,String b)
	 * @功能：输入a、b字符串(String形式的二进制数)，计算其海明距离，并将其返回
	 * */
	public static int HMJL(String a,String b) {
		char[] FW1 = a.toCharArray();//将a每一位都存入数组中
		char[] FW2 = b.toCharArray();//将b每一位都存入数组中
		int haiming=0;
		if(FW1.length == FW2.length){//确保a和b的位数是相同的
			for(int i=0;i<FW1.length;i++){
				if(FW1[i] != FW2[i])//如果该位不同，海明距离加1
					haiming++;
			}
		}
		return haiming;
	}
}